package prototyping.client;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Observable;

public abstract class AbstractClient extends Observable{

	protected ObjectInputStream sInput;
	protected ObjectOutputStream sOutput;
	protected Socket socket;
	
	private String server, username;
	
	/**
	 * @Variable port
	 * @value default port 1500
	 */
	private int port = 1500;
	
	/**
	 * @param server
	 * @param username
	 * This constructor uses the default port 1500
	 */
	public AbstractClient(String server, String username) {
		super();
		this.server = server;
		this.username = username;
	}

	/**
	 * @param server
	 * @param username
	 * @param port
	 * This constructor uses customized port
	 */
	public AbstractClient(String server, String username, int port) {
		super();
		this.server = server;
		this.username = username;
		this.port = port;
	}
	
	
	public boolean start() {
		String msg;
		try {
			socket = new Socket(server, port);
			msg = "Connection accepted " + socket.getInetAddress() + ":" + socket.getPort();
		} 
		catch(Exception ec) {
			try {
				display(new ChatMessage("Error connectiong to server: " + ec));
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
			return false;
		}
		
		try {
			display(new HelpMessage(msg));
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
	
		/*
		 * Creating both objectStreams
		 */
		try{
			sInput  = new ObjectInputStream(socket.getInputStream());
			sOutput = new ObjectOutputStream(socket.getOutputStream());
		}
		catch (IOException eIO){
			
			try {
				display(new ChatMessage("Exception creating new Input/output Streams: " + eIO));
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
			return false;
		}

		/*
		 * Creates thread ServerListener to listen from Server
		 */
		new ServerListener(sInput, this).start();
		
		/*
		 * Send HelpMessage to Server with Default message and username.
		 * Host Adress is captured automatically in HelpMessage.
		 * 
		 * Change HelpMessage to ChatMessage if Class is implemented.
		 */
		try{
			AbstractMessage aMsg = new HelpMessage("Connected!", username);
			sOutput.writeObject(aMsg);
		}
		catch (IOException eIO) {
			
			try {
				display(new ChatMessage("Exception doing login : " + eIO));
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
			disconnect();
			return false;
		}
		return true;
	}
	
	/**
	 * Sends message to either the console or a GUI
	 * @param msg
	 */
	public void display(AbstractMessage msg) {
		setChanged();
		notifyObservers(msg);
	}
	/**
	 * Sends message to Server.
	 * @param msg
	 * @return 
	 */
	public boolean sendMessage(AbstractMessage msg) {
		
		try {
			sOutput.writeObject(msg);
		}
		catch(IOException e) {
			try {
				display(new ChatMessage("Exception writing to server: " + e));
			} catch (UnknownHostException e1) {
				e1.printStackTrace();
			}
		}
		return true;
	}
	
	/**
	 * Closes all input/output streams and disconnects from server.
	 */
	private void disconnect() {
		
		try { 
			if(sInput != null) sInput.close();
		}
		catch(Exception e) {}
		try {
			if(sOutput != null) sOutput.close();
		}
		catch(Exception e) {}
        try{
			if(socket != null) socket.close();
		}
		catch(Exception e) {
			
		}
		
        try {
			display(new ChatMessage("Connection failed!"));
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}
}
